package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric-chaincode-go/shim"
	"github.com/hyperledger/fabric-protos-go/peer"
	"strconv"
	"time"
)

type MarblesChaincode struct{}

type Marble struct {
	ObjectType string `json:"object_type"`
	Name       string `json:"name"`
	Color      string `json:"color"`
	Size       int    `json:"size"`
	Owner      string `json:"owner"`
}

func (t *MarblesChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response {
	return shim.Success(nil)
}

func (t *MarblesChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	funcName, args := stub.GetFunctionAndParameters()

	if funcName == "initMarble" {
		return t.initMarble(stub, args)
	} else if funcName == "readMarble" {
		return t.readMarble(stub, args)
	} else if funcName == "deleteMarble" {
		return t.deleteMarble(stub, args)
	} else if funcName == "transferMarble" {
		return t.transferMarble(stub, args) //args[1]是newOwner
	} else if funcName == "getMarblesByRange" {
		return t.getMarblesByRange(stub, args)
	} else if funcName == "queryMarblesByOwner" {
		return t.queryMarblesByOwner(stub, args)
	} else if funcName == "getHistoryForMarble" {
		return t.getHistoryForMarble(stub, args)
	}

	return shim.Error("没有相应的方法！")
}

// 创建一个弹珠
// []args{marbleName, color, size, owner}
func (t *MarblesChaincode) initMarble(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	marbleName := args[0]

	// 根据marbleName查询弹珠是否被创建
	marbleJsonAsByte, err := stub.GetState(marbleName)
	if err != nil {
		return shim.Error(err.Error())
	}
	if marbleJsonAsByte != nil {
		return shim.Error("marble已经存在！")
	}

	// 如果没有创建，将marble对象转为json格式的字符串，将marble写入到账本中
	size, err := strconv.Atoi(args[2])
	if err != nil {
		return shim.Error("size必须能转化为int类型")
	}
	marble := Marble{ObjectType: "Marble", Name: marbleName, Color: args[1], Size: size, Owner: args[3]}

	marbleJsonAsByte, err = json.Marshal(marble)
	if err != nil {
		return shim.Error(err.Error())
	}
	fmt.Println("marbleJsonAsByte: ", string(marbleJsonAsByte))

	err = stub.PutState(marbleName, marbleJsonAsByte)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(nil)
}

// 从账本中读取一个弹珠
func (t *MarblesChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	marbleName := args[0]
	marbleJsonAsByte, err := stub.GetState(marbleName)
	if err != nil {
		return shim.Error(err.Error())
	} else if marbleJsonAsByte == nil {
		return shim.Success([]byte("marble信息不存在！"))
	}
	return shim.Success(marbleJsonAsByte)
}

// 删除一个弹珠
func (t *MarblesChaincode) deleteMarble(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	marbleName := args[0]
	// 查询弹珠是否存在
	marbleJsonAsByte, err := stub.GetState(marbleName)
	if err != nil {
		return shim.Error(err.Error())
	} else if marbleJsonAsByte == nil {
		return shim.Error("marble不存在！")
	}

	// 根据 marbleName 删除相应弹珠
	err = stub.DelState(marbleName)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(nil)
}

// 更改一个弹珠的owner
func (t *MarblesChaincode) transferMarble(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	marbleName := args[0]
	// 根据 marbleName 查询 marble详情
	marbleJsonAsByte, err := stub.GetState(marbleName)
	if err != nil {
		return shim.Error(err.Error())
	} else if marbleJsonAsByte == nil {
		return shim.Error("marble不存在！")
	}

	//反序列化
	marbleToTransfer := Marble{}
	err = json.Unmarshal(marbleJsonAsByte, &marbleToTransfer) //反序列化时，一定要传入地址
	if err != nil {
		return shim.Error(err.Error())
	}

	// 修改marble的拥有者
	newOwner := args[1]
	marbleToTransfer.Owner = newOwner

	//序列化
	marbleJsonToTransferAsByte, err := json.Marshal(marbleToTransfer)
	if err != nil {
		return shim.Error(err.Error())
	}

	// 写入到账本中
	err = stub.PutState(marbleName, marbleJsonToTransferAsByte)
	if err != nil {
		return shim.Error(err.Error())
	}
	return shim.Success(nil)
}

// 高级查询
// 返回所有名称在指定字典范围内的弹珠信息
func (t *MarblesChaincode) getMarblesByRange(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	startKey, endKey := args[0], args[1]

	//通过起始key和终止key 获取到指定弹珠信息
	resultIterator, err := stub.GetStateByRange(startKey, endKey)
	if err != nil {
		return shim.Error(err.Error())
	}

	defer resultIterator.Close()
	var buffer bytes.Buffer
	buffer.WriteString("[")
	isWrited := false
	//通过迭代将弹珠信息转为JSON数组
	for resultIterator.HasNext() {
		queryResultKV, err := resultIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}

		if isWrited {
			buffer.WriteString(", ")
		}

		//也可以使用fmt.Sprintf()
		buffer.WriteString(`{
			"key" : ` + queryResultKV.Key + `, 
			"value : "` + string(queryResultKV.Value) + `
		}`)

		// 一旦第一次写入了，就更改标记
		isWrited = true
	}
	buffer.WriteString("]")

	fmt.Println(buffer.String())
	return shim.Success(buffer.Bytes())
}

// 返回指定拥有者拥有的所有的弹珠信息
//该查询为富查询，需要支持富查询的数据库（如CouchDB）
func (t *MarblesChaincode) queryMarblesByOwner(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	owner := args[0]
	//查询条件
	//queryString := `{"selector":{"object_type":"Marble","owner":"` + owner + `"}`
	queryString := fmt.Sprintf(`{"selector":{"object_type":"Marble","owner":"%s"}`, owner)
	fmt.Println("queryString:", queryString)

	resultIterator, err := stub.GetQueryResult(queryString)
	if err != nil {
		return shim.Error(err.Error())
	}

	defer resultIterator.Close()
	var buffer bytes.Buffer
	buffer.WriteString("[")
	isWrited := false

	for resultIterator.HasNext() {
		queryResultKV, err := resultIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}

		if isWrited {
			buffer.WriteString(", ")
		}

		marbleString := fmt.Sprintf(`{
			"key" : "%s", 
			"value" : "%s"
		}`, queryResultKV.Key, string(queryResultKV.Value))
		buffer.WriteString(marbleString)

		isWrited = true
	}

	buffer.WriteString("]")
	fmt.Println(buffer.String())
	return shim.Success(buffer.Bytes())
}

// 返回一个弹珠的所有历史信息
func (t *MarblesChaincode) getHistoryForMarble(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	marbleName := args[0]
	resultIterator, err := stub.GetHistoryForKey(marbleName)
	if err != nil {
		return shim.Error(err.Error())
	}

	defer resultIterator.Close()
	var buffer bytes.Buffer
	buffer.WriteString("[")
	isWrited := false

	for resultIterator.HasNext() {
		queryResultKV, err := resultIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}

		if isWrited {
			buffer.WriteString(", ")
		}

		TxId := queryResultKV.TxId
		var value string
		if queryResultKV.IsDelete {
			value = "null"
		} else {
			value = string(queryResultKV.Value)
		}
		timeStamp := time.Unix(queryResultKV.Timestamp.Seconds, int64(queryResultKV.Timestamp.Nanos)).String()
		isDelete := strconv.FormatBool(queryResultKV.IsDelete)

		writedString := fmt.Sprintf(`{
			"TxId" : "%s", 
			"Value" : "%s", 
			"Timestamp" : "%s", 
			"IsDelete" : "%s"
		}`, TxId, value, timeStamp, isDelete)
		buffer.WriteString(writedString)

		isWrited = true
	}
	buffer.WriteString("]")

	fmt.Println(buffer.String())
	return shim.Success(buffer.Bytes())
}

func main() {
	err := shim.Start(new(MarblesChaincode))
	if err != nil {
		fmt.Printf("chaincode strat error!!!")
	}
}
